﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Moq;
using Microsoft.Xrm.Sdk;

namespace XRM.Framework.Test.Unit
{
    #region Enums

    public enum PluginAssembyIsolationMode
    {
        None = 1,
        Sandbox = 2
    }

    public enum PluginMode
    {
        Synchronous = 0,
        Asynchronous = 1
    }

    public enum SdkMessageProcessingStepImage
    {
        PreValidation = 10,
        PreOperation = 20,
        PostOperation = 40
    }

    #endregion
    
    public abstract class PluginUnitTest : XrmUnitTest
    {
        #region Properties

        protected Mock<IServiceProvider> ServiceProviderMock
        {
            get;
            set;
        }

        protected IServiceProvider ServiceProvider
        {
            get
            {
                return ServiceProviderMock.Object;
            }
        }

        protected Mock<IPluginExecutionContext> PluginExecutionContextMock
        {
            get;
            set;
        }

        protected IPluginExecutionContext PluginExecutionContext
        {
            get
            {
                return PluginExecutionContextMock.Object;
            }
        }

        protected Mock<IOrganizationService> OrganizationServiceMock
        {
            get;
            set;
        }

        protected IOrganizationService OrganizationService
        {
            get
            {
                return OrganizationServiceMock.Object;
            }
        }

        protected IPlugin Plugin
        {
            get;
            private set;
        }

        protected Exception Error
        {
            get;
            private set;
        }

        #endregion

        #region Constructors

        public PluginUnitTest()
            : base()
        {
            Init();
        }

        #endregion

        #region Protected Methods

        protected virtual void Init()
        {
            ServiceProviderMock = new Mock<IServiceProvider>();
            OrganizationServiceMock = new Mock<IOrganizationService>();
            PluginExecutionContextMock = new Mock<IPluginExecutionContext>();

            InitPluginExecutionContextMock(PluginExecutionContextMock);

            ServiceProviderMock.Setup(provider => provider.GetService(typeof(ITracingService))).Returns(TracingService);
            ServiceProviderMock.Setup(provider => provider.GetService(typeof(IOrganizationServiceFactory))).Returns(OrganizationServiceFactory);
            ServiceProviderMock.Setup(provider => provider.GetService(typeof(IPluginExecutionContext))).Returns(PluginExecutionContext);

            OrganizationServiceFactoryMock.Setup(factory => factory.CreateOrganizationService(PluginExecutionContext.UserId)).Returns(OrganizationService);
            InitOrganizationServiceMock(OrganizationServiceMock);
        }

        protected void InitPluginExecutionContextMock(Mock<IPluginExecutionContext> mock)
        {
            mock.Setup(context => context.BusinessUnitId).Returns(Guid.NewGuid());
            mock.Setup(context => context.CorrelationId).Returns(Guid.NewGuid());
            mock.Setup(context => context.Depth).Returns(0);
            mock.Setup(context => context.InitiatingUserId).Returns(Guid.NewGuid());
            mock.Setup(context => context.InputParameters).Returns(new ParameterCollection());
            mock.Setup(context => context.IsExecutingOffline).Returns(false);
            mock.Setup(context => context.IsInTransaction).Returns(false);
            mock.Setup(context => context.IsOfflinePlayback).Returns(false);
            mock.Setup(context => context.IsolationMode).Returns((int)PluginAssembyIsolationMode.Sandbox);
            mock.Setup(context => context.MessageName).Returns(string.Empty);
            mock.Setup(context => context.Mode).Returns((int)PluginMode.Synchronous);
            mock.Setup(context => context.OperationCreatedOn).Returns(DateTime.Now);
            mock.Setup(context => context.OperationId).Returns(Guid.NewGuid());
            mock.Setup(context => context.OrganizationId).Returns(Guid.NewGuid());
            mock.Setup(context => context.OrganizationName).Returns(string.Empty);
            mock.Setup(context => context.OutputParameters).Returns(new ParameterCollection());
            mock.Setup(context => context.OwningExtension).Returns(new EntityReference());
            mock.Setup(context => context.PostEntityImages).Returns(new EntityImageCollection());
            mock.Setup(context => context.PreEntityImages).Returns(new EntityImageCollection());
            mock.Setup(context => context.PrimaryEntityId).Returns(Guid.NewGuid());
            mock.Setup(context => context.PrimaryEntityName).Returns(string.Empty);
            mock.Setup(context => context.RequestId).Returns(Guid.NewGuid());
            mock.Setup(context => context.SecondaryEntityName).Returns(string.Empty);
            mock.Setup(context => context.SharedVariables).Returns(new ParameterCollection());
            mock.Setup(context => context.UserId).Returns(Guid.NewGuid());
            mock.Setup(context => context.Stage).Returns((int)SdkMessageProcessingStepImage.PreValidation);
        }

        protected void SetPluginEvent(string primaryEntityName, string messageName, SdkMessageProcessingStepImage stage)
        {
            PluginExecutionContextMock.Setup(context => context.PrimaryEntityName).Returns(primaryEntityName);
            PluginExecutionContextMock.Setup(context => context.MessageName).Returns(messageName);
            PluginExecutionContextMock.Setup(context => context.Stage).Returns((int)stage);
        }

        protected void SetTarget(object target)
        {
            PluginExecutionContext.InputParameters["Target"] = target;
        }

        protected object GetTarget()
        {
            return PluginExecutionContext.InputParameters["Target"];
        }

        protected virtual void Test()
        {
            Plugin = Setup();

            try
            {
                Plugin.Execute(ServiceProvider);
            }
            catch (Exception ex)
            {
                Error = ex;
            }

            Verify();
        }

        #endregion

        #region Abstract Methods

        protected abstract IPlugin Setup();
        protected abstract void Verify();

        #endregion
    }
}
